package com.ruoyi.pay.service.impl;


import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayClient;
import com.alipay.api.AlipayConstants;
import com.alipay.api.internal.util.AlipaySignature;
import com.alipay.api.request.*;
import com.alipay.api.response.*;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.utils.uuid.IdUtils;
import com.ruoyi.pay.entity.OrderInfo;
import com.ruoyi.pay.entity.RefundInfo;
import com.ruoyi.pay.enums.AliPayTradeStatus;
import com.ruoyi.pay.enums.OrderStatus;
import com.ruoyi.pay.mapper.OrderInfoMapper;
import com.ruoyi.pay.service.AliPayService;
import com.ruoyi.pay.service.OrderInfoService;
import com.ruoyi.pay.service.PaymentInfoService;
import com.ruoyi.pay.service.RefundInfoService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.locks.ReentrantLock;

@Service
@Slf4j
public class AliPayServiceImpl implements AliPayService {

    @Resource
    private AlipayClient alipayClient;

    @Resource
    private Environment environment;

    @Resource
    private OrderInfoService orderInfoService;

    @Resource
    private OrderInfoMapper orderInfoMapper;

    @Resource
    private PaymentInfoService paymentInfoService;

    @Resource
    private RefundInfoService refundInfoService;

    private final ReentrantLock lock = new ReentrantLock();

    @Override
    @Transactional
    public String tradeCreate(Long productId) {
        // 生成订单
        OrderInfo orderInfo = orderInfoService.createOrderInfo(productId);
        try {
            // 执行请求 调用支付宝接口
            AlipayTradePagePayRequest request = new AlipayTradePagePayRequest();
            request.setNotifyUrl(environment.getProperty("alipay.notify-url")); // 支付成功后异步通知接口
            request.setReturnUrl(environment.getProperty("alipay.return-url")); // 支付成功后跳转页面

            JSONObject bizContent = new JSONObject();
            bizContent.put("out_trade_no", orderInfo.getOrderNo());  // 商户订单号
            bizContent.put("total_amount", orderInfo.getTotalFee());     // 订单金额
            bizContent.put("subject", orderInfo.getTitle());      // 订单标题
            bizContent.put("product_code", "FAST_INSTANT_TRADE_PAY");   // 销售产品码
            request.setBizContent(bizContent.toJSONString());

            AlipayTradePagePayResponse response = alipayClient.pageExecute(request);
            if (response.isSuccess()) {
                // 支付交易成功
                log.info("调用成功==>{}", response.getBody());

                // 将url地址存入二维码地址
                LambdaUpdateWrapper<OrderInfo> wrapper = new LambdaUpdateWrapper<>();
                wrapper.eq(OrderInfo::getOrderNo, orderInfo.getOrderNo());
                wrapper.set(OrderInfo::getCodeUrl, response.getBody());
                orderInfoService.update(wrapper);

                return response.getBody();
            } else {
                log.error("创建支付交易失败!返回码===》{}，返回描述===》{}", response.getCode(), response.getMsg());
                throw new RuntimeException("创建支付交易失败!");
            }

        } catch (AlipayApiException e) {
            throw new RuntimeException("创建支付交易失败!");
        } catch (Exception e){
            throw new RuntimeException("创建支付交易失败!");
        }
    }

    @Override
    public void SignatureVerification(Map<String, String> params) {
        try {
            boolean signVerify = AlipaySignature.rsaCheckV1(
                    params,
                    environment.getProperty("alipay.alipay-public-key"),
                    AlipayConstants.CHARSET_UTF8,
                    AlipayConstants.SIGN_TYPE_RSA2);

            if (!signVerify) {
                throw new AlipayApiException("<支付异步通知>验签成功");
            }
            log.info("<异步通知>验签成功");
        } catch (AlipayApiException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void paramsSinspect(Map<String, String> params) {
        // 验证应用编号app_id是否为支付宝发给当前商户的唯一标识
        String appId = params.get("app_id");
        // 判断商户编号seller_id
        String sellerId = params.get("seller_id");
        // 商户生成的订单号
        String outTradeNo = params.get("out_trade_no");
        // 商品价格total_amount
        String totalAmount = params.get("total_amount");

        // 检查订单状态
        if (AliPayTradeStatus.TRADE_CLOSED.getType().equals(params.get("trade_status"))) {
            log.error("订单状态不正确,订单状态：{}", params.get("trade_status"));
            throw new RuntimeException("订单状态不正确,订单已关闭!");
        }

        // 本地信息二次校验
        String env_APPID = environment.getProperty("alipay.app-id");
        String env_SELLERID = environment.getProperty("alipay.seller-id");
        OrderInfo orderInfo = orderInfoService.selectByOrderNo(outTradeNo);

        // 如果订单信息不存在 或者 商品金额对不上
        BigDecimal bigDecimal = new BigDecimal(totalAmount);
        if (orderInfo == null || !bigDecimal.equals(orderInfo.getTotalFee())) {
            log.error("商品金额对不上,付款金额:{},本地价格：{}",totalAmount,orderInfo.getTotalFee());
            throw new RuntimeException("支付通知内容异常");
        }

        // 校验应用id和商户ID
        if (!env_APPID.equals(appId) || !env_SELLERID.equals(sellerId)) {
            throw new RuntimeException("支付通知内容异常");
        }

    }

    @Override
    public void processOrder(Map<String, String> params) {
        log.info("处理订单");
        // 获取订单号
        String outTradeNo = params.get("out_trade_no");
        /**
         * 过滤重复通知
         * 加锁防止多线程的并发 避免通知并发问题
         * 接口调用的幂等性 如果订单状态不等于未支付，直接返回成功
         */
        if (lock.tryLock()) {
            try {
                // TODO 查询订单状态 防止重复处理订单
                OrderInfo orderInfo = orderInfoService.selectByOrderNo(outTradeNo);
                String orderStatus = orderInfo.getOrderStatus();
                if (!orderStatus.equals(OrderStatus.NOTPAY.getStatus())) {
                    return;
                }
                // TODO 更新订单状态
                orderInfoService.updateOrderStatus(orderInfo.getOrderNo(), OrderStatus.PAYD.getStatus());
                // TODO 记录支付日志
                paymentInfoService.insertPaymentInfoByNotify(params);
            } finally {
                lock.unlock();
            }
        }
    }

    @Override
    public void cancelOrder(String outTradeNo) {
        // 调用支付宝提供的统一收单交易关闭接口
        this.closeOrder(outTradeNo);

        // TODO 更新用户订单状态
        orderInfoService.updateOrderStatus(outTradeNo, OrderStatus.CLOSED.getStatus());

    }

    @Override
    public String queryOrder(String outTradeNo) {
        log.info("查询订单");
        try {
            AlipayTradeQueryRequest request = new AlipayTradeQueryRequest();
            JSONObject bizContent = new JSONObject();
            bizContent.put("out_trade_no", outTradeNo);

            request.setBizContent(bizContent.toString());

            AlipayTradeQueryResponse response = alipayClient.execute(request);
            if (response.isSuccess()) {
                log.info("查询订单结果 ===》{}", response.getBody());
                return response.getBody();
            } else {
                return null;
            }
        } catch (AlipayApiException e) {
            throw new RuntimeException();
        }
    }

    @Override
    public String queryRefund(String orderNo) {
        log.info("查询订单退款状态");

        try {
            AlipayTradeFastpayRefundQueryRequest request = new AlipayTradeFastpayRefundQueryRequest();
            JSONObject bizContent = new JSONObject();
            bizContent.put("out_trade_no", orderNo);
            bizContent.put("out_request_no", orderNo);
            request.setBizContent(bizContent.toString());

            AlipayTradeFastpayRefundQueryResponse response = alipayClient.execute(request);
            if (response.isSuccess()) {
                log.info("调用查询订单退款结果 ===》{}", response.getBody());
                // 全额退款
                return response.getBody();
            } else {
                log.error("调用查询订单退款结果失败!返回码===》{}，返回描述===》{}", response.getCode(), response.getMsg());
                return null;
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }


    @Override
    public List<OrderInfo> queryTimeOutOrderInfo(int timeout) {

        List<OrderInfo> orderInfos = orderInfoMapper.queryTimeOutOrderInfo(OrderStatus.NOTPAY.getStatus(), timeout);
        return orderInfos;

    }

    /**
     * 如果订单未创建，则更新客户端订单状态
     * 如果订单未支付，则调用关单接口关闭订单，并更新商户端订单状态
     * 如果订单已支付，则更新商户端订单状态 并记录支付日志
     */
    @Override
    public void checkOrderStatus(String outTradeNo) {
        log.info("核实订单状态==>{}", outTradeNo);
        String result = this.queryOrder(outTradeNo);

        // 订单未创建
        if (result == null) {
            log.warn("订单未创建===》{}", outTradeNo);
            // TODO 更新本地订单表状态(关闭)
            orderInfoService.updateOrderStatus(outTradeNo, OrderStatus.CLOSED.getStatus());
            return;
        }

        // 解析查单响应结果
        HashMap<String, Object> map = JSON.parseObject(result, HashMap.class);
        Object alipayTradeQueryResponse = map.get("alipay_trade_query_response");
        HashMap<String, String> response = JSON.parseObject(JSON.toJSONString(alipayTradeQueryResponse), HashMap.class);

        String tradeStatus = response.get("trade_status");

        // 未支付
        if (AliPayTradeStatus.NOTPAY.getType().equals(tradeStatus)) {
            log.info("订单未支付===》{}", outTradeNo);
            // 关闭订单
            this.closeOrder(outTradeNo);
            // TODO 更新商户端订单状态
            orderInfoService.updateOrderStatus(outTradeNo, OrderStatus.CLOSED.getStatus());
        }
        // 订单已支付
        if (AliPayTradeStatus.SUCCESS.getType().equals(tradeStatus)) {
            log.info("订单已支付===》{}", outTradeNo);
            // TODO 更新商户端订单状态
            orderInfoService.updateOrderStatus(outTradeNo, OrderStatus.PAYD.getStatus());

            // TODO 记录支付日志
            paymentInfoService.insertPaymentInfoByOrderInfo(response,outTradeNo);
        }

    }

    /**
     * 申请退款接口
     * @param orderNo 单号
     * @param reason  退款原因
     * @return
     */
    @Override
    @Transactional
    public void refund(String orderNo, String reason) {

        try {
            // 根据订单号查询订单信息
            OrderInfo orderInfo = orderInfoService.selectByOrderNo(orderNo);

            // TODO 创建退款单
            RefundInfo refundInfo = refundInfoService.createRefundInfoForAliPay(orderNo, reason);

            AlipayTradeRefundRequest request = new AlipayTradeRefundRequest();

            JSONObject bizContent = new JSONObject();
            bizContent.put("out_trade_no", orderNo);    // 商户订单号
            bizContent.put("refund_amount", orderInfo.getTotalFee());    // 退款金额
            bizContent.put("refund_reason", reason);    // 退款原因
            request.setBizContent(bizContent.toString());

            AlipayTradeRefundResponse response = alipayClient.execute(request);
            if (response.isSuccess()) {
                log.info("退款成功===》{}", response.getBody());
                // TODO 更新订单状态
                orderInfoService.updateOrderStatus(orderNo, OrderStatus.REFUND.getStatus());
                // TODO 更新退款单记录
                refundInfoService.updateRefundForAliPay(
                        refundInfo.getRefundNo(),
                        response.getTradeNo(),
                        response.getBody(),
                        OrderStatus.REFUND_SUCCESS.getStatus()
                );

            } else {
                log.error("退款失败===》{}", response.getBody());
                // TODO 更新订单状态 (订单改为退款中)
                orderInfoService.updateOrderStatus(orderNo, OrderStatus.REFUND_PROCESSING.getStatus());
                // TODO 更新退款单记录
                refundInfoService.updateRefundForAliPay(
                        refundInfo.getRefundNo(),
                        response.getTradeNo(),
                        response.getBody(),
                        OrderStatus.REFUND_ABNORMAL.getStatus()
                );
            }
        } catch (AlipayApiException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public String queryBillUrl(String billDate, String type) {

        try {
            AlipayDataDataserviceBillDownloadurlQueryRequest request = new AlipayDataDataserviceBillDownloadurlQueryRequest();
            JSONObject bizContent = new JSONObject();
            bizContent.put("bill_date", billDate);
            bizContent.put("bill_type", type);
            request.setBizContent(bizContent.toString());

            AlipayDataDataserviceBillDownloadurlQueryResponse response = alipayClient.execute(request);
            if (response.isSuccess()) {
                log.info("获取账单下载地址成功===》{}", response.getBillDownloadUrl());

                return response.getBillDownloadUrl();
            }
            return null;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }


    /**
     * 关单接口的调用
     * @param outTradeNo
     */
    private void closeOrder(String outTradeNo) {
        log.info("关单接口的调用，订单号 ===》{}", outTradeNo);

        try {
            AlipayTradeCloseRequest request = new AlipayTradeCloseRequest();

            JSONObject bizContent = new JSONObject();
            bizContent.put("out_trade_no", outTradeNo);

            request.setBizContent(bizContent.toString());
            AlipayTradeCloseResponse response = alipayClient.execute(request);

            if (response.isSuccess()) {
                log.info("关单接口调用成功，返回结果 ===》{}", response.getBody());
            }
        } catch (AlipayApiException e) {
            throw new RuntimeException(e);
        }


    }
}
